package com.ijiuchuang.jc_localization.tools.views;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.support.v7.widget.AppCompatImageView;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.Animation;
import android.view.animation.Transformation;

import com.ijiuchuang.jc_localization.R;
import com.ijiuchuang.jc_localization.bean.lite.Station;
import com.ijiuchuang.jc_localization.tools.animation.Ease;
import com.ijiuchuang.jc_localization.tools.animation.EaseInterpolator;
import com.ijiuchuang.jc_localization.tools.utils.MColor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Created by Veev on 2016/6/24
 *
 * @link 384506557@qq.com
 */
public class MapView extends AppCompatImageView {

    private Context mContext;
    private Bitmap mBitmap;
    private Drawable mDrawable;
    private ShapeDrawable shapeDrawable;
    private Bitmap mLocationBitmap;
    /**
     * 属性
     */
    // 控件斜率
    private float viewSlope;
    // 图片斜率
    private float imageSlope;
    // tips文字属性
    private Paint.FontMetrics tipsFontMetrics;
    // 定位图标文字属性
    private Paint.FontMetrics locationFontMetrics;
    // 触点X
    private int touchX = 0;
    // 触点Y
    private int touchY = 0;
    // 原点X
    private int zeroX = 0;
    // 原点Y
    private int zeroY = 0;
    // 图片缩放倍数
    private float zeroScale = 1;
    // 放大镜的半径
    private int magnifyingRadius = 80;
    // 放大倍数
    private int magnifyingFactor = 2;
    private final Matrix matrix = new Matrix();

    // 文字大小 : tips
    private int tipsTextSize = 34;
    // 文字大小 : 定位图标
    private int locationTextSize = 18;
    // 实现tips渐入效果
    private int tipsTextAlpha = 255;
    private int tipsBackAlpha = 138;
    // 动画
    private ProgressAnimation animation;
    private LocationAnimation locationAnimation;
    private float animProgress = 0f;

    /**
     * 画笔
     */
    // 定位图标文字的画笔
    private Paint locationTextPaint;
    // 定位图标画笔
    private Paint locationPaint;
    // 测试点画笔
    private Paint pointPaint;
    // tips文字画笔
    private Paint tipsTextPaint;
    // tips背景画笔
    private Paint tipsBackPaint;
    // 放大镜画笔
    private Paint magnifyingPaint;

    // 测试点
    private Point pointTest;
    // 定位图标坐标 : 当前
    private PointF locationPoint;
    private PointF locationAimPoint;
    private PointF locationOldPoint;

    public MapView(Context context) {
        this(context, null);
    }

    public MapView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MapView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        this.mContext = context;
        if (getDrawable() != null) {
            mBitmap = getBitmapFromDrawable(getDrawable());
        }

        init();
    }

    private void init() {
        // 测试点画笔
        pointPaint = new Paint();
        pointPaint.setColor(Color.RED);
        pointPaint.setAntiAlias(true);// 设置画笔的锯齿效果
        pointPaint.setStrokeWidth(10);
        pointPaint.setStyle(Paint.Style.FILL);

        // tips文字画笔
        tipsTextPaint = new Paint();
        tipsTextPaint.setAntiAlias(true);// 设置画笔的锯齿效果
        tipsTextPaint.setTextSize(tipsTextSize);
        tipsTextPaint.setColor(Color.WHITE);
        // tips背景画笔
        tipsBackPaint = new Paint();
        tipsBackPaint.setColor(Color.BLACK);
        tipsBackPaint.setAntiAlias(true);// 设置画笔的锯齿效果
        tipsBackPaint.setAlpha(tipsBackAlpha);
        // tips动画
        animation = new ProgressAnimation();
        animation.setDuration(500);

        // 放大镜画笔
        magnifyingPaint = new Paint();
        magnifyingPaint.setAntiAlias(true);// 设置画笔的锯齿效果
        magnifyingPaint.setStrokeWidth(1);
        magnifyingPaint.setColor(Color.BLACK);
        magnifyingPaint.setStyle(Paint.Style.STROKE);//空心

        // 定位图标初始化
        mLocationBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pic_location_aa);
        // mLocationBitmap = getColorfulLocationBitmap(mLocationBitmap, 0xFFFA7199);
        locationTextPaint = new Paint();
        locationTextPaint.setColor(Color.BLACK);
        locationTextPaint.setFakeBoldText(true);
        locationTextPaint.setTextSize(locationTextSize);
        locationFontMetrics = locationTextPaint.getFontMetrics();
        locationPaint = new Paint();
        locationPaint.setColor(Color.WHITE);
        locationPaint.setAntiAlias(true);// 设置画笔的锯齿效果

        // 定位动画
        locationAnimation = new LocationAnimation();
        locationAnimation.setDuration(700);
    }

    /**
     * 获取控件高宽比
     *
     * @return slope
     */
    private float getViewSlope() {
        return viewSlope;
    }

    /**
     * 获取图片高宽比
     *
     * @return slope
     */
    private float getImageSlope() {
        imageSlope = (float) mBitmap.getHeight() / mBitmap.getWidth();

        if (mBitmap == null) {
            return 0f;
        } else {
            return imageSlope;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if (getWidth() != 0) {
            viewSlope = (float) getHeight() / getWidth();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (getDrawable() == null) {
            return;
        }

        if (mDrawable != getDrawable()) {
            mDrawable = getDrawable();
            mBitmap = getBitmapFromDrawable(mDrawable);

            BitmapShader shader = new BitmapShader(
                    Bitmap.createScaledBitmap(mBitmap, mBitmap.getWidth() * magnifyingFactor, mBitmap.getHeight() * magnifyingFactor, true),
                    Shader.TileMode.CLAMP,
                    Shader.TileMode.CLAMP);
            // 圆形的drawable
            shapeDrawable = new ShapeDrawable(new RectShape());
            shapeDrawable.getPaint().setShader(shader);
            //shapeDrawable.setBounds(0, 0, magnifyingRadius * 2, magnifyingRadius * 2);
        }

        setZero();

        drawImage(canvas);
        drawPoint(canvas);
        drawMagnifying(canvas);
        drawTips(canvas);
        drawStations(canvas);
        drawLocations(canvas);
    }

    /**
     * 计算坐标原点
     * 图片的左下角为 O 点
     */
    private void setZero() {
        if (getViewSlope() * getImageSlope() != 0) {
            if (getViewSlope() < getImageSlope()) {
                int w = mBitmap.getWidth() * getHeight() / mBitmap.getHeight();
                zeroX = (getWidth() - w) / 2;
                zeroY = getHeight();
                zeroScale = (float) mBitmap.getHeight() / getHeight();
            } else {
                int h = mBitmap.getHeight() * getWidth() / mBitmap.getWidth();
                zeroX = 0;
                zeroY = (getHeight() - h) / 2 + h;
                zeroScale = (float) mBitmap.getWidth() / getWidth();
            }
        }
    }

    /**
     * 绘制地图
     * 居中绘制
     *
     * @param canvas 画布
     */
    private void drawImage(Canvas canvas) {

        if (getViewSlope() * getImageSlope() != 0) {
            if (getViewSlope() < getImageSlope()) {
                int w = mBitmap.getWidth() * getHeight() / mBitmap.getHeight();
                getDrawable().setBounds((getWidth() - w) / 2, 0, (getWidth() - w) / 2 + w, getHeight());
            } else {
                int h = mBitmap.getHeight() * getWidth() / mBitmap.getWidth();
                getDrawable().setBounds(0, (getHeight() - h) / 2, getWidth(), (getHeight() - h) / 2 + h);
            }

            getDrawable().draw(canvas);
        }
    }

    // 基站列表
    private List<Station> stationList = new ArrayList<>();

    public void setStations(Station... stations) {
        for (Station station : stations) {
            stationList.add(station);
        }
    }

    public void clearStations() {
        stationList.clear();
    }

    /**
     * 绘制基站
     *
     * @param canvas 画布
     */
    private void drawStations(Canvas canvas) {
        for (Station station : stationList) {
            if (station.isShown()) {
                int x = (int) (station.getX() / zeroScale + zeroX);
                int y = (int) (zeroY - station.getY() / zeroScale);
                canvas.drawCircle(x, y, 15, pointPaint);
            }
        }
    }

    /**
     * 绘制定位图标
     *
     * @param canvas 画布
     */
    private void drawLocations(Canvas canvas) {
        /*if (pointMap.isEmpty()) {
            return;
        }

        Iterator iterator = pointMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = (Map.Entry) iterator.next();
            String key = (String) entry.getKey();
            PointF val = (PointF) entry.getValue();

            drawLocationView(canvas, key, val);
        }*/

        if (locationPoint != null) {
            drawLocationView(canvas, "监狱", locationPoint);
        }
    }

    /**
     * 绘制定位图标
     *
     * @param canvas 画布
     * @param name   要显示的名称
     * @param pointF 要显示的位置
     */
    private void drawLocationView(Canvas canvas, String name, PointF pointF) {
        // 定位图标的位置
        float locaX = pointF.x / zeroScale + zeroX;
        float locaY = zeroY - pointF.y / zeroScale;

        // 文本的宽高
        float textWidth = locationTextPaint.measureText(name);
        float textHeight = (float) Math.ceil(locationFontMetrics.descent - locationFontMetrics.ascent);

        // 定位图标的宽高
        float viewWidth = mLocationBitmap.getWidth() >= textWidth ? mLocationBitmap.getWidth() : textWidth;
        float viewHeight = mLocationBitmap.getHeight() + textHeight;

        // 绘制点的坐标
        float drawX = locaX - viewWidth / 2;
        float drawY = locaY + textHeight - viewHeight;

        // 文字的坐标
        float textX = drawX + (mLocationBitmap.getWidth() - textWidth) / 2;
        float textY = drawY + mLocationBitmap.getHeight() + locationFontMetrics.descent +
                (locationFontMetrics.descent - locationFontMetrics.ascent) / 2;

        canvas.drawBitmap(mLocationBitmap, drawX, drawY, locationPaint);
        canvas.drawText(name, textX, textY, locationTextPaint);
    }

    private HashMap<String, PointF> pointMap = new HashMap<>();

    // 设置要显示的点
    public void setLocationPointWithName(String name, PointF pointF) {
        // 判断map中是否有该键
        if (pointMap.containsKey(name)) {
            // 若值未改变，则返回，避免不必要的刷新
            if (pointMap.get(name).equals(pointF.x, pointF.y)) {
                return;
            } else {
                pointMap.get(name).set(pointF.x, pointF.y);
            }
        } else {
            // 此处选择 new 一个PointF ，以免重复引用
            PointF point = new PointF(pointF.x, pointF.y);
            pointMap.put(name, point);
        }
        invalidate();
    }

    // 获取定位图标
    public PointF getLocationPoint() {
        return locationPoint;
    }

    // 设置定位图标
    public void setLocationPoint(PointF locationPoint) {
        if (this.locationPoint == null) {
            this.locationPoint = locationPoint;
            this.locationOldPoint = locationPoint;
            this.locationAimPoint = locationPoint;
            invalidate();
        } else {
            // 如果目标位置不是当前的目标
            // 或者目标位置不是当前的位置
            // 才执行后续操作
            this.locationAimPoint = locationPoint;
            // 动画过去
            if (getAnimation() != null && !getAnimation().hasEnded()) {
                locationOldPoint = this.locationPoint;
                clearAnimation();
                startAnimation(locationAnimation);
            } else {
                startAnimation(locationAnimation);
            }
        }
    }

    /**
     * 设置测试点的坐标
     * 该坐标是相对于图片的坐标，而不是相对于控件的坐标
     *
     * @param x 以图片左下角为坐标原点 测试点的 x 坐标
     * @param y 以图片左下角为坐标原点 测试点的 y 坐标
     */
    private void setPointTest(int x, int y) {
        pointTest = new Point(x, y);
        // 设置tips文字
        if (x < 0 || x > mBitmap.getWidth() || y < 0 || y > mBitmap.getHeight()) {
            setTipsText("越界");
        } else {
            setTipsText("X: " + x + "\r\nY: " + y);
        }
    }

    /**
     * 清除测试点
     */
    public void clearPointTest() {
        pointTest = null;
    }

    /**
     * 获取测试点
     *
     * @return 测试点
     */
    public Point getPointTest() {
        return pointTest;
    }

    // 是否显示手指滑动的坐标
    private boolean isPointShown = true;

    public void setPointShown(boolean pointShown) {
        isPointShown = pointShown;
    }

    /**
     * 先拿到测试点相对于图片的坐标
     * 再将坐标转为相对于控件的坐标，也就是需要显示的坐标
     *
     * @param canvas 画布
     */
    private void drawPoint(Canvas canvas) {
        if (isPointShown) {
            if (getPointTest() != null) {
                int x = (int) (pointTest.x / zeroScale + zeroX);
                int y = (int) (zeroY - pointTest.y / zeroScale);
                //canvas.drawPoint(x, y, pointPaint);
                canvas.drawCircle(x, y, 15, pointPaint);
            }
        }
    }

    // 是否显示手指滑动的坐标
    private boolean isTipsShown = true;
    private String tipsText;

    private void setTipsText(String tipsText) {
        this.tipsText = tipsText;
    }

    /**
     * 设置tips是否显示
     *
     * @param tipsShown 是否显示
     */
    public void setTipsShown(boolean tipsShown) {
        isTipsShown = tipsShown;
    }

    private void drawTips(Canvas canvas) {
        if (!TextUtils.isEmpty(tipsText) && isTipsShown && animProgress > 0) {
            tipsFontMetrics = tipsTextPaint.getFontMetrics();
            // 文本的宽度
            float textWidth = tipsTextPaint.measureText(tipsText);
            // 文字的X坐标
            float textX = (getWidth() - textWidth) / 2;
            // 文字的Y坐标
            float textY = getHeight() / 2 + tipsFontMetrics.descent + (tipsFontMetrics.descent - tipsFontMetrics.ascent) / 2;

            // 渐入 - 渐出
            tipsBackPaint.setAlpha((int) (138 * animProgress));
            tipsTextPaint.setAlpha((int) (255 * animProgress));

            canvas.drawRect(textX - 8, textY - tipsTextSize, textX + textWidth + 8, textY + 8, tipsBackPaint);
            canvas.drawText(tipsText, textX, textY, tipsTextPaint);
        }
    }

    // 是否绘制放大镜
    private boolean isMagnifyingShown = true;
    private int magnifyingMode = 0;// 放大镜位置
    public static final int MAGNIFYING_FIXED = 0;   // 固定，在屏幕左上角，可调整
    public static final int MAGNIFYING_FLOAT = 1;   // 浮动，跟随手指

    public void setMagnifyingShown(boolean magnifyingShown) {
        isMagnifyingShown = magnifyingShown;
    }

    /**
     * 绘制放大镜
     *
     * @param canvas 画布
     *               todo pink 设置固定或者浮动
     */
    private void drawMagnifying(Canvas canvas) {
        if (isMagnifyingShown) {
            switch (magnifyingMode) {
                case MAGNIFYING_FIXED:
                    Rect topRect = new Rect(0, 0, magnifyingRadius * 2, magnifyingRadius * 2);
                    Rect bottomRect = new Rect(0, getHeight() - magnifyingRadius * 2, magnifyingRadius * 2, getHeight());

                    // 渐入 - 渐出
                    magnifyingPaint.setAlpha((int) (255 * animProgress));
                    shapeDrawable.setAlpha((int) (255 * animProgress));

                    // 放大镜默认在左下角， 当触点放大镜周围时，移至左上
                    if (touchX < magnifyingRadius * 3 && touchY > getHeight() - magnifyingRadius * 3) {
                        shapeDrawable.setBounds(topRect);
                        shapeDrawable.draw(canvas);
                        canvas.drawLine(0, magnifyingRadius, magnifyingRadius * 2, magnifyingRadius, magnifyingPaint);
                        canvas.drawLine(magnifyingRadius, 0, magnifyingRadius, magnifyingRadius * 2, magnifyingPaint);
                        canvas.drawRect(topRect, magnifyingPaint);
                    } else {
                        shapeDrawable.setBounds(bottomRect);
                        shapeDrawable.draw(canvas);
                        canvas.drawLine(0, getHeight() - magnifyingRadius, magnifyingRadius * 2, getHeight() - magnifyingRadius, magnifyingPaint);
                        canvas.drawLine(magnifyingRadius, getHeight() - magnifyingRadius * 2, magnifyingRadius, getHeight(), magnifyingPaint);
                        canvas.drawRect(bottomRect, magnifyingPaint);
                    }
                    break;
                case MAGNIFYING_FLOAT:
                    break;
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        touchX = (int) event.getX();
        touchY = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 设置点坐标 - 转化为图片的相对坐标
                setPointTest((int) ((touchX - zeroX) * zeroScale), (int) ((zeroY - touchY) * zeroScale));
                // 设置动画 - 渐入效果
                animation.setInterpolator(new EaseInterpolator(Ease.LINEAR_IN));
                // 开启动画
                startAnimation(animation);
                break;
            case MotionEvent.ACTION_MOVE:
                // 设置点坐标
                setPointTest((int) ((touchX - zeroX) * zeroScale), (int) ((zeroY - touchY) * zeroScale));

                // 将要放大的图像平移到触点 - 难点
                // 要注意bitmap的原点是(0,0), 图片的原点是(zeroX, getHeight()-zeroY).
                matrix.setTranslate(magnifyingRadius - (touchX - zeroX) * zeroScale * magnifyingFactor,
                        magnifyingRadius - (touchY - getHeight() + zeroY) * zeroScale * magnifyingFactor);
                shapeDrawable.getPaint().getShader().setLocalMatrix(matrix);
                //shapeDrawable.setBounds(x - magnifyingRadius, y - magnifyingRadius*3, x + magnifyingRadius, y - magnifyingRadius);

                // 重绘
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                // 设置插值器 - 渐出效果
                animation.setInterpolator(new EaseInterpolator(Ease.LINEAR_OUT));
                // 开启动画
                startAnimation(animation);
                break;
        }
        //Log.i("action: " + event.getAction());
        return true;
    }

    // 进度动画
    private class ProgressAnimation extends Animation {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            animProgress = interpolatedTime;

            postInvalidate();
        }
    }

    // 定位图标位移动画
    private class LocationAnimation extends Animation {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);

            float x = locationOldPoint.x + (locationAimPoint.x - locationOldPoint.x) * interpolatedTime;
            float y = locationOldPoint.y + (locationAimPoint.y - locationOldPoint.y) * interpolatedTime;

            locationPoint = new PointF(x, y);

            if (interpolatedTime >= 1.0f) {
                locationOldPoint = locationPoint;
            }
            postInvalidate();
        }
    }

    private Bitmap getBitmapFromDrawable(Drawable drawable) {
        return drawable == null ? null : ((BitmapDrawable) drawable).getBitmap();
    }

    private Bitmap getColorfulLocationBitmap(Bitmap bitmap, int colorful) {
        Bitmap mBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
        int mBitmapWidth = mBitmap.getWidth();
        int mBitmapHeight = mBitmap.getHeight();
        int mArrayColorLength = mBitmapWidth * mBitmapHeight;
        int[] mArrayColor = new int[mArrayColorLength];
        int count = 0;
        for (int i = 0; i < mBitmapHeight; i++) {
            for (int j = 0; j < mBitmapWidth; j++) {
                //如果color 是全透明 或者全黑 返回值为 0
                //getPixel()不带透明通道 getPixel32()才带透明部分 所以全透明是0x00000000
                //而不透明黑色是0xFF000000 如果不计算透明部分就都是0了
                int color = mBitmap.getPixel(j, i);
                //将颜色值存在一个数组中 方便后面修改
                if (color != 0) {
                    mBitmap.setPixel(j, i, MColor.getRandom());  //将白色替换成透明色
                }
            }
        }
        return mBitmap;
    }
}
